Release 10.1A: OpenEdge Development:
Progress 4GL Handbook


Accessing and defining triggers

You can create a trigger procedure in the Procedure Editor, but you attach it to your database in the Data Dictionary, which provides an editor designed to let you code your triggers there as well.

To access triggers:

  1. From the AppBuilder menu, select Tools Data Dictionary and then select a database table such as OrderLine.
  2. Click the Triggers button in the Table Properties dialog box. The Table Triggers dialog box appears:
  3. To see the WRITE trigger that calculated the Extended Price for the OrderLine when it changed, select WRITE from the Event drop-down list:

When you create a new trigger procedure in the Data Dictionary, it supplies the header statement for you. Then you simply write the rest of the code for the procedure. You must give the procedure a name and specify a pathname either in the Procedure field or by selecting the Files button. It is this name that the Data Dictionary associates with the procedure so that Progress executes it at the right times. As you can see, the trigger procedures for the Sports2000 database are located in the directory sports2000trgs under the install directory for OpenEdge. The write trigger for the OrderLine table is called wrordl.p. You can use any naming convention you want for your trigger procedures.

You can click the Help button to access descriptions of all the other buttons here. This section mentions just the two Options that are shown as toggle boxes in the corner of the dialog box.

A trigger procedure provides a certain measure of security that a validation check, or an effect elsewhere in the database, occurs reliably whenever a certain type of update occurs. If you wish to protect yourself against a trigger procedure being replaced by another procedure that doesn’t do the same job, you can check on the Check CRC toggle box. If this option is on, then Progress stores in the metaschema, along with the trigger procedure name, a unique Cyclic Redundancy Check (CRC) identifier for the compiled version of the trigger procedure. Progress raises an error if the r-code file it encounters at run time doesn’t match or if there is no compiled version of the procedure.

You can check on the other toggle box, Overridable, if you want to let the trigger procedure be overridable by a trigger local to a specific application procedure, called a session trigger. Session triggers are discussed briefly in the "Session triggers" section. Session triggers allow you to provide the effects of a trigger but without making it global to the entire application. Among other things, they let you override a trigger procedure with behavior more appropriate to a particular application module. If you don’t check on the Overridable toggle box, then Progress raises an error if a session trigger executes that tries to override the behavior of this trigger procedure.

Having said all this, look at the statement in wrordl.p that calculates the Extended Price:

ExtendedPrice = Price * Qty * (1 - (Discount / 100)). 

Is this a good use for a trigger? Probably not, because it definitely violates the guideline that trigger procedures shouldn’t contain real business logic. In any real application, this kind of price calculation is complex and variable, depending on any number of factors. It’s probably better to provide access to the price calculation algorithm in the application module that controls OrderLine maintenance and to make sure that your application is put together in such a way that the code is always executed when it needs to be. Burying the code in a trigger is not a good thing. Generally, the Sports2000 trigger procedures can serve as examples of how to write triggers, but are often not good examples. This is partly because the database is simplified in ways that are not always realistic and, partly because many of these example procedures predate the architecture for distributed applications and other features that have changed the way you build applications. (Many contain MESSAGE statements, for example, which is definitely a bad idea.)

Using database sequences in CREATE triggers

This section describes database sequences in CREATE triggers.

To see one of the basic uses for triggers, look at an example trigger procedure:

  1. Cancel out of the Table Triggers dialog box and select the Order table.
  2. Click the Triggers button in its Table Properties dialog box. The Table Triggers dialog box appears again:
  3. To assign each Order a unique number, the procedure uses a Database Sequence that stores the latest value assigned to an Order.

  4. Define sequences in the Data Dictionary by clicking the Sequences button in the Data Dictionary main window:

The online help tells you more about how to create sequences in your own database. Basically, each sequence is an Integer value maintained for you in the database. When you define it, you simply give the sequence a name, a starting value, a maximum value, and a value to increment by. Then, each time Progress encounters the NEXT-VALUE function in your application, it increments the sequence and returns the new value, as in the example from this trigger:

ASSIGN  order.ordernum =  NEXT-VALUE(NextOrdNum) 

You can also use the CURRENT-VALUE function to access the current sequence value without incrementing it. Also, there is a CURRENT-VALUE statement that allows you to assign a new value to the sequence:

CURRENT-VALUE ( sequence-name ) = integer-expression. 

You should use the CURRENT-VALUE statement only to reset a sequence in a database that is not being actively used. You should never put such a statement in your application code to assign individual sequence values, as this is not reliable in a multi-user environment.

Both NEXT-VALUE and CURRENT-VALUE allow you to specify a logical database name as an optional second argument if the sequence name might not be unique among all your connected databases.

Assigning a unique Integer key to a new record is the most common use of CREATE triggers.

You can also use a CREATE trigger to assign other initial values that need to be expressions that can’t be represented in the Data Dictionary when you define fields. This trigger assigns the value of the built-in function TODAY to the OrderDate, and TODAY + 14 as the default Promise Date:

order.orderdate = TODAY 
order.promisedate = TODAY + 14 


Copyright © 2005 Progress Software Corporation
www.progress.com
Voice: (781) 280-4000
Fax: (781) 280-4095